package cn.itcast.dewu.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.itcast.dewu.dto.Result;
import cn.itcast.dewu.pojo.Goods;
import cn.itcast.dewu.service.GoodsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.itcast.dewu.pojo.GoodsType;
import cn.itcast.dewu.service.GoodsTypeService;
import cn.itcast.dewu.mapper.GoodsTypeMapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static cn.itcast.dewu.utils.RedisConstants.*;

/**
 * @author Administrator
 * @description 针对表【tb_goods_type】的数据库操作Service实现
 * @createDate 2022-04-29 14:06:30
 */
@Service
public class GoodsTypeServiceImpl extends ServiceImpl<GoodsTypeMapper, GoodsType>
        implements GoodsTypeService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private GoodsService goodsService;
    @Override
    public Result queryByOrderSort() {
        //1.先去缓存中查
        String goodsTypeList = stringRedisTemplate.opsForValue().get(CACHE_LIST_KEY);

        //2.判断是否命中
        if (StrUtil.isNotBlank(goodsTypeList)) {
            if (goodsTypeList.equals("1")) {
                return Result.ok();
            }
            //命中
            List<GoodsType> typeList = JSONUtil.toList(goodsTypeList, GoodsType.class);
            return Result.ok(typeList);
        }
        //3.如果没有则去数据空中查
        List<GoodsType> typeList = query().orderByAsc("sort").list();
        //4.判断数据空中是否存在
        if (typeList.isEmpty()) {
            //5.不存在则返回
            stringRedisTemplate.opsForValue().set(CACHE_LIST_KEY, "1", CACHE_NULL_TTL, TimeUnit.SECONDS);
        }
        //6.存在则返回并且存入到缓存
        String shopJson = JSONUtil.toJsonStr(typeList);
        stringRedisTemplate.opsForValue().set(CACHE_LIST_KEY, shopJson, CACHE_GOODS_TTL, TimeUnit.MINUTES);
        return Result.ok(typeList);
    }

    /*
     * 查询某一类型的商品
     * */
    @Autowired
    private RedissonClient redissonClient;
    @Override
    public Result querTypeOne(Long id) {
        //用于解决缓存穿透的问题
        List<Goods> goods = queryWithMutex(id);
        if (goods == null) {
            return Result.fail("无商品");
        }
        return Result.ok(goods);
    }




    private List<Goods> queryWithMutex(Long id) {
        //先从redis中进行查询是否存在商品信息
        String goodsKey = GOODS_TYPE_ONE+ id;
        String typeJson = stringRedisTemplate.opsForValue().get(goodsKey);
        if (StrUtil.isNotBlank(typeJson)) {
            //缓存中存在商品信息
            List<Goods> goods = JSONUtil.toList(typeJson, Goods.class);
            return goods;
        }
        List<Goods> goods = new ArrayList<>();
        //得到锁对象
        RLock rLock = redissonClient.getLock(GOODS_TYPE_LOCK + id);
        try {
            boolean lock = rLock.tryLock(1L, 10, TimeUnit.SECONDS);
            if (lock) {
                typeJson = stringRedisTemplate.opsForValue().get(goodsKey);
                if (StrUtil.isNotBlank(typeJson)) {
                    //缓存中存在商品信息
                     goods = JSONUtil.toList(typeJson, Goods.class);
                    return goods;
                }
            } else {
                Thread.sleep(50);
                this.queryWithMutex(id);
            }
            //去查询数据库
            goods = goodsService.query().orderByDesc("is_money")
                    .eq("type_id", id).list();
            if (goods == null) {
                //当数据库中没有该商品时将插入一个空值用来防止缓存穿透
                stringRedisTemplate.opsForHash().put(goodsKey,
                        null, null);
                stringRedisTemplate.expire(goodsKey, CACHE_NULL_TTL, TimeUnit.MINUTES);
                return null;
            }
            //缓存重建
          stringRedisTemplate.opsForValue().set(goodsKey,JSONUtil.toJsonStr(goods),GOODS_TYPE_ONE_TTL,TimeUnit.MINUTES);

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //释放锁
            rLock.unlock();
        }
        return goods;
    }

}




